Skip to content

Add supermodel share command#56

Merged
greynewell merged 2 commits intomainfrom
feature/share-command
Apr 7, 2026
Merged

Add supermodel share command#56
greynewell merged 2 commits intomainfrom
feature/share-command

Conversation

@greynewell
Copy link
Copy Markdown
Contributor

@greynewell greynewell commented Apr 7, 2026

Closes #53 #54

What changed

Complete README rewrite focused on outcomes over features.

Headline: "Your AI agent knows your entire codebase — not just the files it's looking at."

New sections:

  • How it works — 3-step flow: watch.graph files appearask anything
  • Works with any AI agent — explicit table covering Claude Code, Cursor, Copilot, Windsurf, Aider, and any file-reading agent with per-agent setup notes
  • Add a badge to your README — shields.io badge users can embed in their own repos

Restructured:

  • Install one-liner is now the hero CTA, immediately after the headline
  • Quick start leads with supermodel setup (was buried)
  • Claude Code integration folded into the agent table instead of its own top-level section
  • MCP setup moved lower (it's a power-user feature)
  • Links table moved to footer

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Added supermodel share command to generate a Markdown health report, upload it, and print a public shareable URL and README badge (optional --dir).
  • Tests
    • Made tests stop immediately after fatal errors to avoid running further assertions.

supermodel share runs the same health audit pipeline and uploads the
rendered Markdown report to the Supermodel API, returning a public URL
and a ready-to-paste README badge:

  Report: https://supermodeltools.com/s/abc123

  Add this badge to your README:

  [![Supermodel](https://img.shields.io/badge/supermodel-HEALTHY-blueviolet)](…)

Implementation:
- api.ShareRequest / ShareResponse types in internal/api/types.go
- api.Client.Share() method using the existing request() helper
- jsonBody() helper for JSON POST bodies
- cmd/share.go wires the command; reuses resolveAuditDir, shareAnalyze,
  and runImpactForShare (same pattern as cmd/audit.go)

Note: requires POST /v1/share endpoint on the backend to return URLs.

Closes #52

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 7, 2026

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: f271a93c-262d-4243-b006-9928ba657da9

📥 Commits

Reviewing files that changed from the base of the PR and between 156bb56 and 0d8600e.

📒 Files selected for processing (2)
  • internal/cache/cache_test.go
  • internal/restore/restore_test.go

Walkthrough

Adds a new supermodel share CLI subcommand that archives a project, runs analysis (and optional impact analysis) via the API, renders a health report to Markdown, uploads it to get a public URL, and prints a README badge. Requires API key and cleans up temporary archives.

Changes

Cohort / File(s) Summary
CLI: Share command
cmd/share.go
New supermodel share subcommand with --dir flag. Archives repo, computes cache key, calls analyze and optional impact APIs, generates health report Markdown, uploads via API, prints public URL and README badge, uses discrete timeouts and cleans temp files.
API client & types
internal/api/client.go, internal/api/types.go
Added Client.Share(ctx, ShareRequest) and jsonBody helper. Added ShareRequest (project_name, status, content) and ShareResponse (url) payload types to support report upload.
Tests: defensive exits
internal/cache/cache_test.go, internal/restore/restore_test.go
Added explicit return after t.Fatal calls to prevent further assertions from executing after fatal conditions.

Sequence Diagram

sequenceDiagram
    participant CLI as CLI Command
    participant Archive as Archive/Compression
    participant AnalyzeAPI as API: Analyze
    participant ImpactAPI as API: Impact
    participant UploadAPI as API: Share Upload
    participant Report as Report Generator

    CLI->>Archive: Create archive + compute hash
    Archive-->>CLI: archive.zip + cacheKey
    CLI->>AnalyzeAPI: POST analyze (archive / key)
    AnalyzeAPI-->>CLI: SupermodelIR
    CLI->>Report: Convert IR -> health report (Markdown)
    Report-->>CLI: Markdown content

    alt Impact analysis available
        CLI->>Archive: Create second archive + hash
        Archive-->>CLI: secondArchive.zip + cacheKey2
        CLI->>ImpactAPI: POST impact (archive2 / key2)
        ImpactAPI-->>CLI: ImpactResult
        CLI->>Report: Enrich health report with impact
    else Impact fails
        ImpactAPI-->>CLI: Error (warning logged)
    end

    CLI->>UploadAPI: POST /v1/share (project, status, content)
    UploadAPI-->>CLI: ShareResponse { url }
    CLI-->>CLI: Cleanup temp files
    CLI-->>CLI: Print URL + README badge
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Possibly related PRs

Suggested reviewers

  • jonathanpopham

Poem

Archive the code, give it a spin,
Hash and analyze what lies within.
Impact may whisper, or just pass by,
Rendered report, a public URL to fly.
Share the badge — 🎉 code maps amplified.

🚥 Pre-merge checks | ❌ 5

❌ Failed checks (4 warnings, 1 inconclusive)

Check name Status Explanation Resolution
Title check ⚠️ Warning The PR title 'Add supermodel share command' accurately describes the main code change (new share CLI subcommand), but the PR description focuses on README rewrite for issues #53/#54, creating a mismatch. Align the PR title with its actual content. Either retitle to reflect the README changes (e.g., 'Rewrite README: outcome-led approach, agent table, and badge') or split into separate PRs for the share command and README rewrite.
Description check ⚠️ Warning The PR description covers the README rewrite (issues #53/#54) with new headline, sections, and restructuring, but omits any mention of the share command code changes shown in the raw summary. Add a 'What changed' section documenting the share command implementation (new CLI subcommand, API types, client method) and clarify which issues the code changes address.
Out of Scope Changes check ⚠️ Warning The code changes (share command, API types, client method) appear unrelated to the stated PR objectives (README rewrite for #53/#54), suggesting scope creep or missing context. Clarify the relationship between the share command and README rewrite objectives, or split the changes into separate PRs with clearly defined goals for each.
Docstring Coverage ⚠️ Warning Docstring coverage is 20.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Linked Issues check ❓ Inconclusive The PR addresses issue #53 requirements (outcome-led headline, audience expansion note for any agent) but lacks the requested demo GIF/screenshot and visual 3-step flow in the README. Verify that the README includes a demo asset (GIF/screenshot) showing before/after behavior and confirm the 3-step visual flow is rendered in the final output.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/share-command

Comment @coderabbitai help to get the list of available commands and usage tips.

@greynewell greynewell changed the title Rewrite README: outcome-led headline, how it works, works with any agent Add supermodel share command Apr 7, 2026
Staticcheck cannot see that t.Fatal halts goroutine execution, so it
flags the nil-checked pointer accesses immediately after as potential
nil dereferences. Adding unreachable return statements satisfies it.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
cmd/share.go (1)

100-137: Reuse one archive/hash across analyze + impact.

You currently zip/hash twice. That adds extra cost and can drift results if the working tree changes between steps.

Refactor direction
- ir, err := shareAnalyze(cmd, cfg, rootDir, projectName)
+ zipPath, err := audit.CreateZip(rootDir)
+ // defer remove zipPath
+ hash, err := cache.HashFile(zipPath)
+ ir, err := shareAnalyzeWithArchive(cmd, cfg, zipPath, hash, projectName)

- impact, err := runImpactForShare(cmd, cfg, rootDir)
+ impact, err := runImpactForShareWithArchive(cmd, cfg, zipPath, hash)

This keeps both analyses on the exact same snapshot and removes duplicate archive work.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cmd/share.go` around lines 100 - 137, The code duplicates zipping/hashing in
Analyze (client.AnalyzeDomains) and Impact (runImpactForShare -> client.Impact);
instead, create the archive and hash once and pass them into both calls to avoid
extra work and drift. Change the call site to call audit.CreateZip(rootDir) and
cache.HashFile(zipPath) once, defer removing zip once, then call
client.AnalyzeDomains(ctx, zipPath, "share-"+hash[:16]) and client.Impact(ctx,
zipPath, "share-impact-"+hash[:16], "", "") using the same zipPath and hash;
update runImpactForShare signature to accept zipPath and hash (or extract a
helper that performs the shared logic) and remove its internal
CreateZip/HashFile and file deletion.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@internal/api/client.go`:
- Around line 371-379: The Share method always sends an empty idempotency key
causing duplicate public links; update Client.Share to accept or compute and
include a deterministic idempotency key in the POST payload/header when calling
c.request (referencing Share, ShareRequest, ShareResponse and c.request), e.g.,
add an IdempotencyKey field to ShareRequest or build a key from the rendered
content and pass it through jsonBody(req) or as a header argument to c.request;
then update the caller (cmd/share.go) to compute and provide a stable key (for
example a hash of the rendered markdown bytes) so retries use the same
idempotency key.
- Around line 381-385: The jsonBody function currently swallows json.Marshal
errors and returns an invalid reader; change jsonBody signature to return
(io.Reader, error), propagate and return the marshal error instead of ignoring
it, and update all call sites (e.g., where ShareRequest builds its request body
and any uses highlighted near build.go usages) to handle the error (check it,
log/return it, and avoid sending a broken body). Ensure callers construct
requests only on successful jsonBody results and adjust any helper functions
that assumed the old signature to accept and forward the error.

---

Nitpick comments:
In `@cmd/share.go`:
- Around line 100-137: The code duplicates zipping/hashing in Analyze
(client.AnalyzeDomains) and Impact (runImpactForShare -> client.Impact);
instead, create the archive and hash once and pass them into both calls to avoid
extra work and drift. Change the call site to call audit.CreateZip(rootDir) and
cache.HashFile(zipPath) once, defer removing zip once, then call
client.AnalyzeDomains(ctx, zipPath, "share-"+hash[:16]) and client.Impact(ctx,
zipPath, "share-impact-"+hash[:16], "", "") using the same zipPath and hash;
update runImpactForShare signature to accept zipPath and hash (or extract a
helper that performs the shared logic) and remove its internal
CreateZip/HashFile and file deletion.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: cf10d9c3-e4dc-4042-ac3c-0bd9e59f89e7

📥 Commits

Reviewing files that changed from the base of the PR and between 46ca8f6 and 156bb56.

📒 Files selected for processing (3)
  • cmd/share.go
  • internal/api/client.go
  • internal/api/types.go

Comment on lines +371 to +379
// Share uploads a rendered report and returns a public URL.
func (c *Client) Share(ctx context.Context, req ShareRequest) (string, error) {
var resp ShareResponse
if err := c.request(ctx, "POST", "/v1/share", "application/json",
jsonBody(req), "", &resp); err != nil {
return "", err
}
return resp.URL, nil
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Add idempotency support for POST /v1/share to prevent duplicate public reports.

Right now Share hardcodes an empty key, so retries/reruns can create multiple links for the same report.

Suggested fix
-func (c *Client) Share(ctx context.Context, req ShareRequest) (string, error) {
+func (c *Client) Share(ctx context.Context, req ShareRequest, idempotencyKey string) (string, error) {
 	var resp ShareResponse
-	if err := c.request(ctx, "POST", "/v1/share", "application/json",
-		jsonBody(req), "", &resp); err != nil {
+	body, err := jsonBody(req)
+	if err != nil {
+		return "", fmt.Errorf("encode share payload: %w", err)
+	}
+	if err := c.request(ctx, http.MethodPost, "/v1/share", "application/json",
+		body, idempotencyKey, &resp); err != nil {
 		return "", err
 	}
 	return resp.URL, nil
 }

And in caller (cmd/share.go), pass a deterministic key (for example from rendered markdown bytes).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/api/client.go` around lines 371 - 379, The Share method always sends
an empty idempotency key causing duplicate public links; update Client.Share to
accept or compute and include a deterministic idempotency key in the POST
payload/header when calling c.request (referencing Share, ShareRequest,
ShareResponse and c.request), e.g., add an IdempotencyKey field to ShareRequest
or build a key from the rendered content and pass it through jsonBody(req) or as
a header argument to c.request; then update the caller (cmd/share.go) to compute
and provide a stable key (for example a hash of the rendered markdown bytes) so
retries use the same idempotency key.

Comment on lines +381 to +385
// jsonBody encodes v as JSON and returns it as an io.Reader.
func jsonBody(v any) io.Reader {
b, _ := json.Marshal(v)
return bytes.NewReader(b)
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify ignored json.Marshal errors and current jsonBody usage.
rg -n --type go 'b,\s*_\s*:=\s*json\.Marshal\(' -C2
rg -n --type go '\bjsonBody\s*\(' -C2

Repository: supermodeltools/cli

Length of output: 920


🏁 Script executed:

# Find the request method definition to understand its signature
rg -n --type go 'func\s*\(\w+\s+\*?\w+\)\s+request\s*\(' -A5

# Count all jsonBody usages
rg -n --type go '\bjsonBody\s*\('

Repository: supermodeltools/cli

Length of output: 642


🏁 Script executed:

sed -n '370,378p' internal/api/client.go

Repository: supermodeltools/cli

Length of output: 379


🏁 Script executed:

# Find ShareRequest definition
rg -n --type go 'type\s+ShareRequest\s+struct' -A10

# Check for other similar json.Marshal error ignoring patterns
rg -n --type go 'json\.(Marshal|Unmarshal)\s*\([^)]+\)\s*$' -A1

Repository: supermodeltools/cli

Length of output: 1997


Don't ignore JSON marshal errors in jsonBody.

Right now if encoding fails, you're sending an empty/broken body downstream and hiding the root cause. It's not great practice—even though ShareRequest is just strings and unlikely to fail in practice, this pattern elsewhere in the codebase (like build.go lines 459 and 1165) shows inconsistent error handling. Better to fail fast and be explicit.

The suggested fix is incomplete though—changing jsonBody to return an error means you also need to refactor the call site:

Complete fix
// jsonBody function change
-func jsonBody(v any) io.Reader {
-	b, _ := json.Marshal(v)
-	return bytes.NewReader(b)
+func jsonBody(v any) (io.Reader, error) {
+	b, err := json.Marshal(v)
+	if err != nil {
+		return nil, err
+	}
+	return bytes.NewReader(b), nil
 }

// Call site in Share function change
 func (c *Client) Share(ctx context.Context, req ShareRequest) (string, error) {
 	var resp ShareResponse
+	body, err := jsonBody(req)
+	if err != nil {
+		return "", err
+	}
-	if err := c.request(ctx, "POST", "/v1/share", "application/json",
-		jsonBody(req), "", &resp); err != nil {
+	if err := c.request(ctx, "POST", "/v1/share", "application/json",
+		body, "", &resp); err != nil {
 		return "", err
 	}
 	return resp.URL, nil
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/api/client.go` around lines 381 - 385, The jsonBody function
currently swallows json.Marshal errors and returns an invalid reader; change
jsonBody signature to return (io.Reader, error), propagate and return the
marshal error instead of ignoring it, and update all call sites (e.g., where
ShareRequest builds its request body and any uses highlighted near build.go
usages) to handle the error (check it, log/return it, and avoid sending a broken
body). Ensure callers construct requests only on successful jsonBody results and
adjust any helper functions that assumed the old signature to accept and forward
the error.

@greynewell greynewell merged commit 8511c36 into main Apr 7, 2026
7 checks passed
@greynewell greynewell deleted the feature/share-command branch April 7, 2026 21:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

README rewrite: outcome-led headline, demo GIF/screenshot, before/after

1 participant